package an_sintatico;

import an_lexico.AnLexico;
import an_lexico.Token;
import an_semantico.AnSemantico;
import java.util.Stack;

/**
 *
 * @author fernando
 */
public class AnSintatico {


    /**
     *
     */
    private AnSemantico semantico;

    /**
     *
     */
    private AnLexico lexico;

    /**
     * One State by line, One token Type by Collumn
     */
    private int col,  lin;

    /**
     * Transition Table (of all sub-machines)
     * One State by line, One token Type by Collumn
     */
    private TransSintatica transTable[/*col*/][/*lin*/];

    /**
     * Initial State of Each Sub Machine
     */
    private int subMachineInitialState[];


	/**
	 *
     */
    private String subMachineID[];

    /**
     *
     */
    private int finalStates[];

    /**
     *
     */
    private int tokenTypes[/*col*/]; //Type of Token to be treated by each Transition Table collumn

    /**
     *
     */
    private Stack<TransSintatica> pilhaDeChamadas;//Info of the callers of this sub machine

    /**
     *
     */
    private int currentState;

    /**
     *
     */
    private Stack<Integer> currentSubMachine; //Does Nothing, only used to ease the debugging.

    /**
     *
     */
    protected void setSubMachineID(String[] subMachineID) {
        this.subMachineID = subMachineID;
    }

    /**
     *
     */
    protected void setFinalStates(int[] finalStates) {
        this.finalStates = finalStates;
    }

    /**
     * Set the initial state of eache sub machine.
     * @param subMachineInitialState
     */
    protected void setSubMachineInitialState(int[] subMachineInitialState) {
        this.subMachineInitialState = subMachineInitialState;
    }

    /**
     * set - number of collumns in the transition table (Token Types accepted by the
     * machine).
     * @param col
     */
    protected void setCol(int col) {
        this.col = col;
    }

    /**
     * set - number of lines in the transition table (Each line is an state).
     * @param lin
     */
    protected void setLin(int lin) {
        this.lin = lin;
    }

    /**
     * Set an translation table beteween AnLexico and AnSintatico type of tokens.
     * @param tokenType
     */
    protected void setTokenTypes(int[] tokenType) {
        this.tokenTypes = tokenType;
    }

    /**
     * Set the transition table.
     * @param transTable
     */
    protected void setTransTable(TransSintatica[][] transTable) {
        this.transTable = transTable;
    }

    /**
     * Initializes only the lexical and semantic analizers associated with this
     * class and some variables that have to be initialized for every language.
     * @param lex - Lexical analiser used to get the tokens.
     * @param sem - Semantical analiser that generates the code.
     */
    public AnSintatico(AnLexico lex, AnSemantico sem) {
        this.lexico = lex;
        this.semantico = sem;
        pilhaDeChamadas = new Stack<TransSintatica>();
        pilhaDeChamadas.add(new TransSintatica(-2, -1, -1));
        currentSubMachine = new Stack<Integer>();
        currentSubMachine.push(0);
    }

    /**
     * Print info about the AnSintatico.
     */
    public void printAnSintatico() {
        System.out.println("\n\nAnalisador Sintatico");
        System.out.print(lin + " " + col);
        System.out.println("\nTabela de Transição - proximo estado");
        for (int i = 0; i < lin; i++) {
            System.out.print(" " + (i >= 10 ? i : " " + i));
            for (int j = 0; j < col; j++) {
                if (transTable[j][i].getNextState() != -1) {
                    System.out.print(" " + ((transTable[j][i].getNextState() >= 10) ? transTable[j][i].getNextState() : " " + transTable[j][i].getNextState()));
                } else {
                    System.out.print("  x");
                }
            }
            System.out.println("");
        }
        System.out.println("\nTabela de Transição - sub maquinas");
        for (int i = 0; i < lin; i++) {
            System.out.print(" " + (i >= 10 ? i : " " + i));
            for (int j = 0; j < col; j++) {
                if (transTable[j][i].getSubMaquina() != -1) {
                    System.out.print(" " + ((transTable[j][i].getSubMaquina() >= 10) ? transTable[j][i].getSubMaquina() : " " + transTable[j][i].getSubMaquina()));
                } else {
                    System.out.print("  x");
                }
            }
            System.out.println("");
        }
        System.out.println("\nTokens Types Used");
        for (int i = 0; i < col; i++) {
            System.out.print(" " + tokenTypes[i]);
        }
        System.out.print("\n" + subMachineInitialState.length + " Initial States:");
        for (int i = 0; i < subMachineInitialState.length; i++) {
            System.out.print(" " + subMachineInitialState[i]);
        }
        System.out.print("\n" + subMachineInitialState.length + " Final States:");
        for (int i = 0; i < finalStates.length; i++) {
            if(finalStates[i] > 0){
              System.out.print(" " + i);
            }
        }
        System.out.print("\n");
    }

    /**
     * analize the source code and generates code(using the semantic analizer).
     */
    public void analyze() {
        Token t = lexico.getToken();

        semantico.initializeCode();

        while (!pilhaDeChamadas.empty()) {
            System.out.println("Token:" + lexico.getTokenID(t.getTokenType()) + " Token ID:" + t.getID());

            int tokenCollum = findTransitionCollumn(t.getTokenType());
            TransSintatica nextts;
            if( tokenCollum == -1 ){
                nextts = new TransSintatica(-1,-1,-1);
            }else{
                nextts = transTable[tokenCollum][currentState];
            }
            printTransitionInfo(tokenCollum, nextts);
            /*End Info*/

            if (nextts.getSubMaquina() > 0) { //Chamada de Sub Maquina
                subMachineCall(nextts, t);
            } else if (nextts.getNextState() < 0) { //Retorno de Sub Maquina
                nextts = subMachineReturn(nextts, t);
            } else { //Transicao normal de estados
                currentState = nextts.getNextState();
            }

            semantico.geraCodigo(nextts.getSemanticFunction(), t);
            t = lexico.getToken();
        }
        if(!pilhaDeChamadas.empty() || t.getTokenType() != 0){
            throw new Error("Stack not empty, or end of file not reached");
        }
    }

    /**
     * Search the translation table(tokenType[]) for the collumn of the Transition
     * Table to be used in this transition.
     * @param tokenType - Token to be consumed.
     * @return Transition Table to be used
     * @throws Error if there isnt an corresponding Transition Table Collumn.
     */
    private int findTransitionCollumn(int tokenType) {
        boolean ok = false;
        boolean tableEnd = false;
        int i = 0;

        if(tokenType == 0){
            return -1;
        }

        while (!ok && !tableEnd) {
            //System.out.println("this.tokenType=" + this.tokenType[i] + "tokenType=" + tokenType);
            if (i >= col) {
                throw new Error("AnSintatico - Collumn Not Found - Token " + tokenType + " collumn" + i);
            } else if (this.tokenTypes[i] == tokenType) {
                ok = true;
            } else {
                i++;
            }
        }

        if (tableEnd) {
            throw new Error("This token type does not exist");
        } else {
            System.out.println("TokenCollumn = " + i);
            return i;
        }
    }

    /**
     * Returns the collumn of the token type in the transition table.
     * @param tokenType
     * @return
     */
    protected int getTokenTypeCollumn(int tokenType) {
        int i = 0;
        for (int t : tokenTypes) {
            if (t == tokenType) {
                return i;
            }
            i++;
        }
        throw new Error("Token type:" + tokenType + "/" + lexico.getTokenID(tokenType) + " does not exist in the transition table.");
    }

    /**
     *
     */
    private void printTransitionInfo(int tokenCollum, TransSintatica nextts) {

        /* Info*/
        System.out.print("SubMaquina: " + subMachineID[currentSubMachine.peek()]);
        System.out.print(" Estado: " + (currentState - subMachineInitialState[currentSubMachine.peek()]));
        System.out.print(" TokenCollun: " + tokenCollum);
        System.out.println("semanticFunction = " + nextts.getSemanticFunction());
        if (nextts.getNextState() >= 0) {
            System.out.print(" Next State:" + (nextts.getNextState() - subMachineInitialState[currentSubMachine.peek()]));
        }
        if (nextts.getSubMaquina() > -1) {
            System.out.println(" Next SubMachine: " + subMachineID[nextts.getSubMaquina()]);
        } else {
            System.out.println("");
        }
        System.out.println("");
    }

    /**
     *
     */
    private void subMachineCall(TransSintatica nextts, Token t) {
        //Chamada de Sub Maquina
        pilhaDeChamadas.push(nextts);
        currentSubMachine.push(nextts.getSubMaquina());
        System.out.println("Give Token Back - Sub Machine Call");
        lexico.setNextToken(t);
        currentState = subMachineInitialState[nextts.getSubMaquina()];
    }

    /**
     *
     */
    private TransSintatica subMachineReturn(TransSintatica nextts, Token t) throws Error {
        //Retorno de Sub Maquina
        if (nextts.getNextState() == -1) {
            if (finalStates[currentState] == 1) {
                //falta ação do Semantico
                /**
                 * Como uma transicao vazia e' adicionada no comeco,
                 * a pilha nao deve ficar vazia antes de sair do loop.
                 * Ou seja, antes de retornar a sub maquina principal.
                 */
                if (!pilhaDeChamadas.empty()) {
                    nextts = pilhaDeChamadas.pop();
                    System.out.println("Give Token Back - Sub Machine Return");
                    lexico.setNextToken(t);
                    currentSubMachine.pop();
                } else {
                    throw new Error("Erro - Empty Stack");
                }
                currentState = nextts.getNextState();
                //falta ação do Semantico
            } else {
                throw new Error("This type of token was not expected in this place: " + lexico.getTokenID(t.getTokenType()));
            }
        }
        return nextts;
    }
}